(*  :Title:	Solution of Differential Equations via Laplace ransforms. *)

(*  :Authors:	Wally McClure, Brian Evans, James McClellan  *)

(*
    :Summary:	This package will solve linear constant coefficient
		differential equations by means of the Laplace transform.
 *)

(*  :Context:	SignalProcessing`Analog`LSolve`  *)

(*  :PackageVersion:  2.7	*)

(*
    :Copyright:	Copyright 1990-1991 by Brian L. Evans
		Georgia Tech Research Corporation

	Permission to use, copy, modify, and distribute this software
	and its documentation for any purpose and without fee is
	hereby granted, provided that the above copyright notice
	appear in all copies and that both that copyright notice and
	this permission notice appear in supporting documentation,
	and that the name of the Georgia Tech Research Corporation,
	Georgia Tech, or Georgia Institute of Technology not be used
	in advertising or publicity pertaining to distribution of the
	software without specific, written prior permission.  Georgia
	Tech makes no representations about the suitability of this
	software for any purpose.  It is provided "as is" without
	express or implied warranty.
 *)

(*
    :History:	Date started -- September 28, 1990 (adapted from ZSolve.m)
		Handle arbitrary initial conditions -- May, 1991
 *)

(*  :Keywords:	linear constant-coefficient differential equations	*)

(*  :Source:	{Operational Mathematics} by Ruel Churchill  *)

(*  :Warning:	*)

(*  :Mathematica Version:  1.2 or 2.0  *)

(*  :Limitation:  *)

(*  :Discussion:  *)

(*  :Functions:   PartialL  LSolve  *)



(*  B E G I N     P A C K A G E  *)

BeginPackage[ "SignalProcessing`Analog`LSolve`",
	      "SignalProcessing`Analog`LaPlace`",
	      "SignalProcessing`Analog`InvLaPlace`",
	      "SignalProcessing`Analog`LSupport`",
	      "SignalProcessing`Support`TransSupport`",
	      "SignalProcessing`Support`ROC`",
	      "SignalProcessing`Support`SigProc`",
	      "SignalProcessing`Support`SupCode`" ]


If [ TrueQ[ $VersionNumber >= 2.0 ],
     Off[ General::spell ];
     Off[ General::spell1 ] ];


(*  U S A G E     I N F O R M A T I O N  *)

LSolve::usage =
	"LSolve[ diffequ == drivingfun, y[t] ] solves the differential \
	equation diffequ = drivingfun, where diffequ is a linear constant \
	coefficient differential equation and drivingfun is the driving \
	function (a function of t). \
	Thus, diffequ has the form a0 y[t] + a1 y'[t] + .... \
	One can specify initial values; e.g., \
	LSolve[ y''[t] + 3/2 y'[t] + 1/2 y[t] == Exp[a t], \
	y[t], y[0] -> 4, y'[0] -> 10 ]. \
	A differential equation of N terms needs N-1 initial conditions. \
	All unspecified conditions are considered to be zero. \
	LSolve can justify its answers."

(*  E N D     U S A G E     I N F O R M A T I O N  *)


Begin[ "`Private`" ]


(*  Messages  *)
LSolve::nolaplace =
	"The forcing function `` does not have a valid Laplace ransform."
LSolve::toomany =
	"Initial/boundary conditions occur at more than two locations in ``."
PartialL::nolaplace =
	"The differential equation `` does not have a partial Laplace transform."


(*  completetransform  *)
completetransform[ a_. y_ + b_., y_, x_ ] := ( x - b ) / a /; FreeQ[b, y]


(* ic2index --  extract location of initial/bondary condition *)
SetAttributes[ic2index, Listable]
ic2index[ (y_[index_] -> value_), y_ ] := index
ic2index[ (Derivative[n_][y_][index_] -> value_), y_ ] :=  index


(* introduction *)
introduction[dialogue_, diffequ_, drivingfun_, bvflag_, initlist_, y_[t_]] :=
	Block [ {},
		If [ dialogue ||  SameQ[dialogue, All],
		     Print["Solving for ",y[t]," in the differential equation"];
		     Print["  ", diffequ, " = ", drivingfun];
		     If [ bvflag,
		          Print["  subject to the boundary conditions"],
		          Print["  subject to the initial conditions"] ];
		     If [ EmptyQ[initlist],
			  Print["  being zero."],
		          Print["  ", initlist ] ] ] ]


(* preprocess *)
preprocess[ left_ == right_, y_[t_] ] :=
	Block [	{diffequ, drivingfun},

		diffequ = Select[ left - right, validterm[y[t]] ];
		drivingfun = -(left - right - diffequ);

		diffequ == drivingfun ]


(* printdialogue *)
printdialogue[dialogue_, y_[t_], Ypartial_, X_, s_, bvflag_] :=
	Block [	{formula, ll, n, rules, term1, term2, term3, terml, ul, YYstr},

		(* formula for Laplace transform of nth derivative of y(t) *)
		YYstr = ToString[StringForm["L { `` }", y[t]]];
		term1 = s^n YYstr;
		If [ bvflag,
		     rules = { ul -> "ul", ll -> "ll" };
		     term2 = s^(n-1) ( y[ul] Exp[-s ul] - y[ll] Exp[-s ll] );
		     term3 = s^(n-2) ( y'[ul] Exp[-s ul] - y'[ll] Exp[-s ll] );
		     terml = ( Derivative[n-1][y][ul] Exp[-s ul] -
			       Derivative[n-1][y][ll] Exp[-s ll] ),

		     rules = { ll -> "t0" };
		     term2 = - s^(n-1) y[ll] Exp[-s ll];
		     term3 = - s^(n-2) y'[ll] Exp[-s ll];
		     terml = - Derivative[n-1][y][ll] Exp[-s ll] ];
		rules = rules ~Join~ { s -> "s", n -> "n" };

		term1 = term1 /. rules;
		term2 = term2 /. rules;
		term3 = term3 /. rules;
		terml = terml /. rules;	     (* yeh, this was a lot of work *)

		(* dialogue for user *)
		If [ dialogue,
		     Print["After taking the Laplace transform of both sides"];
		     Print["and solving for the transform of ", y[t], ":" ] ];
		If [ SameQ[dialogue, All],
		     Print[ " " ];
		     Print[ "The Laplace transform of the left side is:" ];
		     Print[ "  ",
		            ( Ypartial /. YY -> YYstr ) /. s -> "s" ];
		     Print[ "( In the general case, the unilateral Laplace" ];
		     Print[ "  transform of the nth derivative of ",
			    y[t], " is:" ];
		     Print[ "  L {", Derivative["n"][y][t], " } = ", term1,
			    " + ", term2, " + ", term3, " + ... + ", terml ];
		     If [ bvflag,
		          Print["  where t=ll is the lower limit and t=ul is"];
			    Print["  the upper limit of the boundary conditions. )"],
			  Print["  where t=t0 is the initial condition. )"] ];
		     Print[ " " ];
		     Print[ "The Laplace transform of the right side is:" ];
		     Print[ "  ", X /. s -> "s" ];
		     Print[ "Solving for the unknown transform yields" ] ] ]


(* validic --  valid initial/bondary condition? *)
validic [y_[t_]] [ y_[index_] -> value_ ] := True
validic [y_[t_]] [ Derivative[n_][y_][index_] -> value_ ] := True


(* validterm --  valid term in left-hand side of a differential equation? *)
validterm [y_[t_]] [ a_. y_[t_ + t0_.] ] := True
validterm [y_[t_]] [ a_. Derivative[n_][y_][t_] ] := True




(*  PartialL  *)
PartialL[ x_ + v_, rest__ ] :=
	PartialL[ x, rest ] + PartialL[ v, rest ]

PartialL[ a_ x_, y_[t_], s_, YY_, rest___ ] :=
	a PartialL[ x, y[t], s, YY, rest ] /; FreeQ[a, t]

PartialL[ Derivative[1][y_][t_ + a_.], y_[t_], s_, YY_, ll_, Infinity ] :=
	Exp[a s] ( s YY - y[ll] Exp[-s ll] ) /;
	FreeQ[a, t]

PartialL[ Derivative[1][y_][t_ + a_.], y_[t_], s_, YY_, ll_, ul_ ] :=
	Exp[a s] ( s YY + y[ul] Exp[-s ul] - y[ll] Exp[-s ll] ) /;
	FreeQ[a, t] && ! SameQ[ul, Infinity]

PartialL[ Derivative[n_][y_][t_ + a_.], y_[t_], s_, YY_, ll_, Infinity ] :=
	Block [	{i, otherterms, term, term1, term2},
		term1 = s^n YY;
		term2 = - s^(n-1) y[ll] Exp[-s ll];
		term = - Derivative[i][y][ll] Exp[-s ll];
		otherterms = Apply[Plus, Table[s^(n-i-1) term, {i, 1, n-1}]];
		Exp[a s] ( term1 + term2 + otherterms ) ] /;
	FreeQ[a, t] && n > 1

PartialL[ Derivative[n_][y_][t_ + a_.], y_[t_], s_, YY_, ll_, ul_ ] :=
	Block [	{i, otherterms, term, term1, term2},
		term1 = s^n YY;
		term2 = s^(n-1) ( y[ul] Exp[-s ul] - y[ll] Exp[-s ll] );
		term = ( Derivative[i][y][ul] Exp[-s ul] -
			 Derivative[i][y][ll] Exp[-s ll] );
		otherterms = Apply[Plus, Table[s^(n-i-1) term, {i, 1, n-1}]];
		Exp[a s] ( term1 + term2 + otherterms ) ] /;
	FreeQ[a, t] && n > 1 && ! SameQ[ul, Infinity]

PartialL[ y_[t_ + a_.], y_[t_], s_, YY_, rest___ ] :=
	Exp[a s] YY /; FreeQ[a, t]

PartialL[ a_, y_[t_], s_, YY_, rest___ ] :=
	Message[ PartialL::nolaplace, a ] /; FreeQ[a, t]




(*  LSolve  *)
LSolve/: Options[LSolve] := { Dialogue -> True }

LSolve[ y_[t_ + t0_.] == drivingfun_, y_[t_] ] :=
	( drivingfun /. { t -> t - t0 } ) /; FreeQ[t0, t]

LSolve[ left_ == right_, y_[t_], init___ ] :=
	Block [	{auginitlist, bvflag, dialogue, diffequ, drivingfun,
		 equationlist, indexlist, initfun, lowerlimit, options,
		 rminus, rplus, s, sobj, upperlimit, X, Y, Ycollected,
		 Ypartial, YY, YYstr},

		(* Parse options, determine the level of Dialogue requested *)
		options = ToList[init] ~Join~ Options[LSolve];
		dialogue = Replace[Dialogue, options];
		initlist = Select[ options, validic[y[t]] ];
		auginitlist = initlist ~Join~
			      { y[t0_] :> 0, Derivative[n_][y][t0_] :> 0 };

		(* Collect terms  *)
		equationlist = preprocess[left == right, y[t]];
		diffequ = First[equationlist];
		drivingfun = Second[equationlist];

		(* Find the location in t of initial/boundary conditions *)
		If [ EmptyQ[initlist],
		     bvflag = False;
		     lowerlimit = 0;
		     upperlimit = Infinity,

		     indexlist = Union[ ic2index[initlist, y] ];
		     If [ Length[indexlist] > 2,
		          Message[ LSolve::toomany, t ];
		          indexlist = Take[indexlist, 2] ];
		     indexlist = Sort[indexlist];
		     lowerlimit = First[indexlist];
		     bvflag = ( Length[indexlist] == 2 );
		     upperlimit = If [ bvflag, Second[indexlist], Infinity ] ];

		(* Tell the user about the differential equation *)
		introduction[ dialogue, diffequ, drivingfun,
			      bvflag, initlist, y[t] ];

		(* Find complete Laplace transform of driving function *)
		sobj = LaPlace[ drivingfun, t, s, Dialogue -> False ];
		If [ ! SameQ[Head[sobj], LTransData],
		     Message[ LSolve::nolaplace, drivingfun ];
		     Return[] ];
		rminus = GetRMinus[sobj];		      (* R- of X(s) *)
		rplus = GetRPlus[sobj];			      (* R+ of X(s) *)
		X = TheFunction[sobj];		(* X(s) from Laplace object *)

		(* Find partial Laplace transform of differential equation *)
		Ypartial = PartialL[ diffequ, y[t], s, YY,
				     lowerlimit, upperlimit ];
		Ycollected = Collect[Ypartial, YY];

		(* Dialogue taking Laplace transform of both sides to user *)
		printdialogue[dialogue, y[t], Ycollected, X, s, bvflag];

		(* Inverse transform complete Laplace transform of y, Y(s) *)
		Y = completetransform[Ycollected, YY, X];

		If [ SameQ[dialogue, All],
		     Print[ "  ", Y /. s -> "s" ];
		     Print[ "which becomes" ] ];

		(*  Replace initial conditions in Y(s) with values *)
		Y = Y /. auginitlist;

		If [ dialogue || SameQ[dialogue, All],
		     Print[ "  ", Y /. s -> "s" ];
		     Print[ "Inverse transforming this gives ", y[t], ":" ] ];

		InvLaPlace[{Y, rminus, rplus}, s, t, Dialogue -> False] ]


(*  E N D     P A C K A G E  *)

End[]
EndPackage[]

If [ TrueQ[ $VersionNumber >= 2.0 ],
     On[ General::spell ];
     On[ General::spell1 ] ];


(*  H E L P     I N F O R M A T I O N  *)

Combine[ SPfunctions, { LSolve } ]
Protect[ LSolve ]


(*  E N D I N G     M E S S A G E  *)

Print[ "LSolve, a differential equation solver, is loaded." ]
Null
